Voorbeeldoplossing Serialization Book
Om de serialiseringstechnieken in te oefenen gaan we dezelfde methodes, die we gemaakt hebben voor Postcode, nu maken voor Book.
Opdracht
- Sla de XML hieronder op in een bestand met de naam Book.xml in de Data map.
In het voorbeeld heb ik het bestand van Digitap gedownloaded. - Maak een klassenbestand met de naam Book.cs, met daarin de klasse
Book
in de namespaceDotNetCore.Learning
. Baseer je op de structuur van het XML bestand om een Book object te maken met de overeenkomstige properties (de structuur staat beschreven in Ontwerpfase Book App).using System; using System.Collections.Generic; using System.Text; namespace DotNetCore.Learning { public class Book { private string title; public string Title { get { return title; } set { title = value; } } private string year; public string Year { get { return year; } set { year = value; } } private string city; public string City { get { return city; } set { city = value; } } private string publisher; public string Publisher { get { return publisher; } set { publisher = value; } } private string author; public string Author { get { return author; } set { author = value; } } private string edition; public string Edition { get { return edition; } set { edition = value; } } private string translator; public string Translator { get { return translator; } set { translator = value; } } private string comment; public string Comment { get { return comment; } set { comment = value; } } private List<Book> list; public List<Book> List { get { return list; } set { list = value; } } } }
- Maak een klassenbestand met de naam BookSerializing.cs met daarin de klasse
BookSerializing
in de namespaceDotNetCore.Learning
.using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Xml.Serialization; namespace DotNetCore.Learning { class BookSerializing { } }
- In deze klasse maak je de volgende methoden:
- Vermits de opgegeven dataset in XML is beginnen we met een methode die het XML bestand deserialiseert naar een generieke lijst van boeken
List<Book>
. De naam van deze methode isDeserializeFromXml
. - Code:
public static List<Book> DeserializeFromXml() { XmlSerializer serializer = new XmlSerializer(typeof(Book[])); Book book = new Book(); StreamReader file = new System.IO.StreamReader(@"Data/Book.xml"); Book[] books = (Book[])serializer.Deserialize(file); file.Close(); // array converteren naar List return new List<Book>(books); }
- Schrijf een methode die de boeken in de generieke lijst toont in de console met de naam
ShowBooks
.public static void ShowBooks(List<Book> list) { foreach (Book item in list) { Console.WriteLine("{0}{1}{2}{3}{4}{5}{6}{7}", item?.Title, item?.Year, item?.City, item?.Publisher, item?.Author, item?.Edition, item?.Translator, item?.Comment); } }
- Uitproberen in Program.cs:
using DotNetCore.Learning; using System; using System.Collections.Generic; namespace DontNetCore.Learning { class Program { static void Main(string[] args) { Console.WriteLine("Gegevensserialisatie"); List<Book> books = BookSerializing.DeserializeFromXml(); BookSerializing.ShowBooks(books); // TryOut.SerializeObjectToCsv(books, ";"); } } }
- We krijgen de volgende foutmelding:
InvalidOperationException: <Books xmlns=''> was not expected.
- We openen het Book.xml bestand in Kladblok en stellen vast dat de XML declaratie wel degelijk in het bestand staat. Je kan die manueel verwijderen uit het tekstbestand. Maar we zoeken op of het niet mogelijk is om dat programmatorisch te doen.
- Informatie over de fout opzoeken op het internet.
Het eerste antwoord op de vraag is al meteen een goed antwoord! We moeten aan de XmlSerializer opgeven wat het root element is van onze XML. - Verbeteren:
public static List<Book> DeserializeFromXml() { XmlRootAttribute xRoot = new XmlRootAttribute(); xRoot.ElementName = "Books"; // xRoot.Namespace = "http://www.cpandl.com"; xRoot.IsNullable = true; XmlSerializer serializer = new XmlSerializer(typeof(Book[]), xRoot); Book book = new Book(); StreamReader file = new System.IO.StreamReader(@"Data/Book.xml"); Book[] books = (Book[])serializer.Deserialize(file); file.Close(); // array converteren naar List return new List<Book>(books); }
Hiermee geven we aan dat deXmlSerializer
de XML declaratie moet overslaan en met het root elementBooks
moet beginnen. - Video 1
- En opnieuw uitproberen: We hebben nu een ander probleem. Niet alle boeken worden correct ingelezen. Er staat een lege lijn en spaties aan het begin. Hoe pakken we dit probleem aan?
- We beginnen met de debuggen. We gaan kijken wat er precies wordt ingelezen in de books list. Zet een breakpoint naast de programmaregel waar de lijst wordt aangemaakt:
- Opnieuw runnen. Het programma stopt op regel 12. Klik op step over om regel 12 uit te voeren en inspecteer vervolgens de
books
list: Vervolgens op je rij 22, dat is de rij waar problemen zijn: - Vanwaar komt die new line en die spaties in de
Author
property? We open Book.xml opnieuw in Kladblok: - Dat is m.i. een fout in de
XmlSerializer
klasse. We kunnen dat oplossen door gebruik te maken van de setters van deBook
klasse. Vooraleer een waarde toe te kennen aanAuthor
enTitle
gaan we kijken als er new lines, dubbele spaties en spaties aan het begin in staan. Indien dit geval is, verwijderen we ze. Ik geef hier de setters voorAuthor
enTitle
:public string Author { get { return author; } set { author = value.Replace("\n", string.Empty).Replace(" ", " ").TrimStart(); } } public string Title { get { return title; } set { title = value.Replace("\n", string.Empty).Replace(" ", " ").TrimStart(); } }
Voor alle zekerheid pas je best alle setters aan. - Verwijder de breakpoint en opnieuw runnen met dit als resultaat:
- We krijgen de volgende foutmelding:
- Maak een methode met de naam
SerializeListToCsv
die een generieke lijst met boeken serialiseert naar een CSV bestand.
Deze mthode is gebaseerd op de methodeSerializeObjectToCsv
die staat in Een generieke lijst serialiseren naar CSV.public static string SerializeListToCsv(List<Book> list, string fileName, string separator) { string message; try { TextWriter writer = new StreamWriter(fileName); foreach (Book item in list) { // One of the most versatile and useful additions to the C# language in version 6 // is the null conditional operator ?. writer.WriteLine("{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}{13}{14}", item?.Title, separator, item?.Year, separator, item?.City, separator, item?.Publisher, separator, item?.Author, separator, item?.Edition, separator, item?.Translator, separator, item?.Comment); } writer.Close(); message = $"Het bestand met de naam {fileName} is gemaakt!"; } catch (Exception e) { // Melding aan de gebruiker dat iets verkeerd gelopen is. // We gebruiken hier de nieuwe mogelijkheid van C# 6: string interpolatie message = $"Kan het bestand met de naam {fileName} niet maken.\nFoutmelding {e.Message}."; } return message; }
- Maak een methode met de naam
DeserializeCsvToList
die een csv bestand omzet naar een lijst van het typeList<Book>
.
Dit is iets ingewikkelder omdat we geen bestaande bibliotheek gebruiken.- Een methode om een tekstbestand in te lezen
We gebruiken daarvoor de methodeLees
. De uitleg vind op Tekstbestanden inlezen.
Maak een klassenbestand met de naam Tekstbestand.cs en plak daarin onderstaande code. Let erop dat de klasse staat in de namespaceHelpers
. - Een methode om Book.csv in te lezen
Deze methode is gebaseerd op de methodeReadPostcodesFromCSVFile
uit CSV bestand deserialiseren naar een generieke lijst.public static string ReadBooksFromCSVFile() { Helpers.Tekstbestand bestand = new Helpers.Tekstbestand(); bestand.FileName = @"Data/Book.csv"; bestand.Lees(); return bestand.Text; }
- Nu nog de methode die de string gegevens van een boek omzet naar een
Book
object
Deze methode is gebaseerd op de methodePostcodeCsvToObject
uit CSV bestand deserialiseren naar een generieke lijst.public static Book BookCsvToObject(string line, string separator) { Book book = new Book(); string[] values = line.Split(separator); book.Title = values[0]; book.Year = values[1]; book.City = values[2]; book.Publisher = values[3]; book.Author = values[4]; book.Edition = values[5]; book.Translator = values[6]; book.Comment = values[7]; return book; }
-
Tenslotte de
DeserializeCsvToList
methode maakt een lijst van book-objecten. DeReadPostcodesFromCSVFile
methode retourneert een lange string. Elke regel in de string bevat de gegevens over 1 boek. We moeten die string dus splitsen op basis van een new line (\n
) en elke regel stoppen we in een array. Dan doorlopen we elk element van de array. Elk element is een string met waarden die door een ; gescheiden zijn. Die string geven we door aan deBookCsvToObject
methode om die te splitsen en zo bekomen waarden in eenBook
object te plaatsen.
Deze methode is gebaseerd opReadPostcodesFromCSVFile
uit CSV bestand deserialiseren naar een generieke lijst.public static List<Book> DeserializeCsvToList(string separator) { string[] books = ReadBooksFromCSVFile().Split('\n'); List<Book> list = new List<Book>(); foreach (string s in books) { if (s.Length > 0) { list.Add(BookCsvToObject(s, separator)); } } return list; }
- Een methode om een tekstbestand in te lezen
- Video 2
- Maak een methode met de naam
SerializeListToJson
die een generieke lijst met boeken serialiseert naar een json bestand.- Installeer de laatste versie van Newtonsog.Json in NuGet Package Manager Console:
Install-Package Newtonsoft.Json -Version 12.0.3
- De code is gebaseerd op de
SerializeCsvToJson
methode uit JSON en .NET Core.public static void SerializeListToJson(List<Book> list, string fileName) { TextWriter writer = new StreamWriter(fileName); // static method SerilizeObject van Newtonsoft.Json string postcodeString = Newtonsoft.Json.JsonConvert.SerializeObject(list); writer.WriteLine(postcodeString); writer.Close(); }
- Uitproberen in de
Main
methode van deProgram
klasse:using DotNetCore.Learning; using System; using System.Collections.Generic; namespace DontNetCore.Learning { class Program { static void Main(string[] args) { Console.WriteLine("Gegevensserialisatie"); List<Book> books = BookSerializing.DeserializeFromXml(); BookSerializing.ShowBooks(books); TryOut.SerializeListToCsv(books, @"Data/Book.csv", ";"); books = TryOut.DeserializeCsvToList(";"); Console.WriteLine("".PadLeft(60, '*')); Console.WriteLine("\n\n\nIngelezen van CSV bestand!\n\n\n"); Console.WriteLine("".PadLeft(60, '*')); BookSerializing.ShowBooks(books); TryOut.SerializeListToJson(books, @"Data/Book.json"); } } }
- Als we kijken naar het gegenereerde json bestand stellen we vast dat er iets niet klopt:
- Hoe komt dat? We openen het CSV bestand dat we inlezen:
- We herschrijven de code in de
Main
methode van deProgram
klasse en gebruiken een andere separatorusing DotNetCore.Learning; using System; using System.Collections.Generic; namespace DontNetCore.Learning { class Program { static void Main(string[] args) { Console.WriteLine("Gegevensserialisatie"); List<Book> books = BookSerializing.DeserializeFromXml(); BookSerializing.ShowBooks(books); TryOut.SerializeListToCsv(books, @"Data/Book.csv", "|"); books = TryOut.DeserializeCsvToList("|"); Console.WriteLine("".PadLeft(60, '*')); Console.WriteLine("\n\n\nIngelezen van CSV bestand!\n\n\n"); Console.WriteLine("".PadLeft(60, '*')); BookSerializing.ShowBooks(books); TryOut.SerializeListToJson(books, @"Data/Postcode.json"); } } }
- Ziet er veel beter uit:
- Er bestaan online JSON editors. Knip en plak de inhoud van Book.json in de editor:
- Installeer de laatste versie van Newtonsog.Json in NuGet Package Manager Console:
- Maak een methode met de naam
DeserializeJsonToList
die een json bestand omzet naar een lijst van het typeList<Book>
.- De code is gebaseerd op de
GetPostcodeListFromJson
methode uit JSON en .NET Core .public static List<Book> DeserializeJsonToList(string fileName) { Helpers.Tekstbestand bestand = new Helpers.Tekstbestand(); bestand.FileName = fileName; bestand.Lees(); List<Book> list = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Book>>(bestand.Text); return list; }
- Uitproberen in de
Main
methode van deProgram
klasse:using DotNetCore.Learning; using System; using System.Collections.Generic; namespace DontNetCore.Learning { class Program { static void Main(string[] args) { Console.WriteLine("Gegevensserialisatie"); List<Book> books = BookSerializing.DeserializeFromXml(); BookSerializing.ShowBooks(books); TryOut.SerializeListToCsv(books, "|"); books = TryOut.DeserializeCsvToList("|"); Console.WriteLine("".PadLeft(60, '*')); Console.WriteLine("\n\n\nIngelezen van CSV bestand!\n\n\n"); Console.WriteLine("".PadLeft(60, '*')); BookSerializing.ShowBooks(books); TryOut.SerializeListToJson(books, @"Data/Book.json"); books = TryOut.DeserializeJsonToList(@"Data/Book.json"); Console.WriteLine("".PadLeft(60, '*')); Console.WriteLine("\n\n\nIngelezen van JSON bestand!\n\n\n"); Console.WriteLine("".PadLeft(60, '*')); BookSerializing.ShowBooks(books); } } }
- Resultaat:
- De code is gebaseerd op de
- Vermits de opgegeven dataset in XML is beginnen we met een methode die het XML bestand deserialiseert naar een generieke lijst van boeken
- Video 3
- Probeer elk van deze methoden uit in de
main
methode van deProgram
klasse in Program.cs.
Ik heb die methoden al hierboven uitgeprobeerd. Maar je kan het nog anders doen, door bijvoorbeeld een andere seperator te gebruiken. Of een XML bestand in te lezen zonder XML declaratie, enz.